#include "socket.hpp"
#include <memory>
#include <sys/select.h>
#include <algorithm>
#include <sys/time.h>
#include <iostream>
#include <string.h>

const static int defaultfd = -1;

class SelectServer
{
    const static int N = sizeof(fd_set) * 8;

public:
    SelectServer(uint16_t port)
        : _port(port), _listensock(std::make_unique<net::TcpSocket>())
    {
        InetAddr addr("0", _port);
        _listensock->BuildServerSocket(addr);
        for (int i = 1; i < N; i++)
        {
            sockfd[i] = defaultfd;
        }
        sockfd[0] = _listensock->SockFd();
    }

    void Loop()
    {
        while (true)
        {
            fd_set readfds;
            FD_ZERO(&readfds);
            int maxsockfd = _listensock->SockFd();
            for (int i = 0; i < N; i++)
            {
                if (sockfd[i] != defaultfd)
                {
                    FD_SET(sockfd[i], &readfds);
                    maxsockfd=std::max(maxsockfd,sockfd[i]);
                }
            }
            struct timeval timeout = {1, 0};
            int n = select(maxsockfd + 1, &readfds, nullptr, nullptr, &timeout);
            if (n == 0)
            {
                //Log(INFO, "time out,%d.%d", timeout.tv_sec, timeout.tv_usec);
            }
            else if (n < 0)
            {
                Log(WARNING, "%s", strerror(errno));
            }
            else
            {
                Log(DEBUG, "event happen");
                // 监听到有文件被写入
                execute(readfds);
            }
        }
    }

    void execute(fd_set &readfds)
    {
        for (int i = 0; i < N; i++)
        {
            if (FD_ISSET(sockfd[i], &readfds))
            {
                if (sockfd[i] == _listensock->SockFd())//Accept
                {
                    InetAddr addr;
                    std::shared_ptr<net::Socket> ptr = _listensock->Accept(&addr);
                    Log(DEBUG,"get a new link.sockfd%d client info:%s:%d",ptr->SockFd(),addr.ip().c_str(),addr.port());
                    int pos = 1;
                    while (pos < N)
                    {
                        if (sockfd[pos] == defaultfd)
                        {
                            sockfd[pos] = ptr->SockFd();
                            break;
                        }
                        pos++;
                    }
                    if (pos == N)
                    {
                        ptr->Close();
                        Log(ERROR, "The number of open files has exceeded the upper limit");
                    }
                    else
                    {
                        Log(DEBUG, "sockfd:%d add sockfdarray suceess",ptr->SockFd());
                    }
                }
                else
                {
                    int fd=sockfd[i];
                    char buffer[2048];
                    int n=recv(fd,buffer,sizeof(buffer)-1,0);
                    if(n>0)
                    {
                        buffer[n]='\0';
                        std::cout<<"client say#"<<buffer<<std::endl;
                    }
                    else if(n==0)//当返回值为0时说明对方关闭了连接
                    {
                        close(fd);
                        sockfd[i]=defaultfd;//从辅助数组中移除 这样就不用监听这个连接了
                        Log(INFO,"a client quit fd:%d",fd);
                    }
                    else
                    {
                        Log(ERROR,"%s",strerror(errno));
                    }
                    std::string response("server echo#");
                    response+=buffer;
                    send(fd,response.c_str(),response.size(),0);
                }
            }
        }
    }

private:
    uint16_t _port;
    std::unique_ptr<net::Socket> _listensock;
    int sockfd[N];
};
const int SelectServer::N;
